/*!
    \copyright  Copyright (c) 2025 Qualcomm Technologies International, Ltd.\n
                All Rights Reserved.\n
                Qualcomm Technologies International, Ltd. Confidential and Proprietary.
    \file       usb_audio_fd.c
    \ingroup    usb_audio
    \brief      USB Audio Function Driver
*/

#include "usb_audio_fd.h"
#include "usb_audio_class_10.h"
#include "usb_audio_class_20.h"

#include <panic.h>
#include "kymera.h"
#include "volume_messages.h"
#include "telephony_messages.h"
#include "ui.h"
#include <device.h>
#include <device_list.h>
#include "device_types.h"
#include "device_properties.h"

#include "usb_source.h"
#include "usb_audio_source_audio_if/usb_audio_source_audio_if.h"

#define USB_AUDIO_MSG_SEND(task, id, msg)       if(task != NULL) \
                                                   task->handler(task, id, msg)

Task usb_audio_client_cb[USB_AUDIO_REGISTERED_CLIENT_COUNT];

bool UsbAudio_NotifyClient(usb_audio_registered_client_t client,
                           MessageId message_id, Message message)
{
    Task client_task = usb_audio_client_cb[client];

    if (client_task)
    {
        client_task->handler(client_task, message_id, message);
    }
    return client_task != NULL;
}


void UsbAudio_DisconnectAudioMsg(usb_audio_info_t *usb_audio)
{
    if(!usb_audio->headphone->source_connected)
    {
        return;
    }

    /* This is error case, if we dont have Media Player client and Audio
     * cant be played, it should have not happend.
     */

    usb_audio->headphone->source_connected = FALSE;

    /* with respect to audio context, host has just connected but not actively streaming */
    UsbSource_SetAudioContext(context_audio_connected);

    DEBUG_LOG_ALWAYS("USB Audio: Audio Disconnected");

    PanicFalse(
            UsbAudio_NotifyClient(USB_AUDIO_REGISTERED_CLIENT_MEDIA,
                                  USB_AUDIO_DISCONNECTED_IND,
                                  (Message)(&usb_audio->headphone->audio_source))
    );

    UsbAudio_NotifyClient(USB_AUDIO_REGISTERED_CLIENT_STATUS,
                          USB_AUDIO_DISCONNECTED_IND,
                          (Message)(&usb_audio->headphone->audio_source));
}

void UsbAudio_ConnectAudioMsg(usb_audio_info_t *usb_audio)
{
    if(usb_audio->is_pending_delete || usb_audio->headphone->source_connected)
    {
        return;
    }
    /* This is error case, if we dont have Media Player client and Audio
     * cant be played, it should have not happend.
     */
    usb_audio->headphone->source_connected = TRUE;

    /* Updating MRU index of usb_device */
    DeviceList_DeviceWasUsed(UsbAudio_GetDevice());

    /* Since USB Audio source can't reliably determine whether the context is streaming, playing,
     * or paused. So, once alternate audio interface is selected, setting the context as streaming.
     * Limitation: USB Audio source shall not be routed if there is an another source with playing context. */
    UsbSource_SetAudioContext(context_audio_is_streaming);

    DEBUG_LOG_ALWAYS("USB Audio: Audio Connected");

    PanicFalse(
            UsbAudio_NotifyClient(USB_AUDIO_REGISTERED_CLIENT_MEDIA,
                                  USB_AUDIO_CONNECTED_IND,
                                  (Message)(&usb_audio->headphone->audio_source))
    );

    UsbAudio_NotifyClient(USB_AUDIO_REGISTERED_CLIENT_STATUS,
                          USB_AUDIO_CONNECTED_IND,
                          (Message)(&usb_audio->headphone->audio_source));
}


void UsbAudio_DisconnectVoiceMsg(usb_audio_info_t *usb_audio)
{
    if(!usb_audio->headset->source_connected)
    {
        return;
    }
    /* This is error case, if we dont have Media Player client and Audio
     * cant be played, it should have not happend.
     */
    usb_audio->headset->source_connected = FALSE;

    /* with respect to audio context, host has just connected but not actively streaming */
    UsbSource_SetVoiceState(USB_SOURCE_VOICE_CONNECTED);

    DEBUG_LOG_ALWAYS("USB Audio: Voice Disconnected");

    /* Inform Media player as speaker is in placed */
    PanicFalse(
            UsbAudio_NotifyClient(USB_AUDIO_REGISTERED_CLIENT_TELEPHONY,
                                  TELEPHONY_AUDIO_DISCONNECTED,
                                  (Message)(&usb_audio->headset->audio_source))
    );
}

void UsbAudio_ConnectVoiceMsg(usb_audio_info_t *usb_audio)
{
    if(usb_audio->is_pending_delete || usb_audio->headset->source_connected )
    {
        return;
    }
    /* This is error case, if we dont have Media Player client and Audio
     * cant be played, it should have not happend.
     */
    usb_audio->headset->source_connected = TRUE;

    /* Updating MRU index of usb_device */
    DeviceList_DeviceWasUsed(UsbAudio_GetDevice());

    /* with respect to audio context, this means host is actively streaming audio */
    UsbSource_SetVoiceState(USB_SOURCE_VOICE_ACTIVE);

    DEBUG_LOG_ALWAYS("USB Audio: Voice Connected");

    PanicFalse(
            UsbAudio_NotifyClient(USB_AUDIO_REGISTERED_CLIENT_TELEPHONY,
                                 TELEPHONY_AUDIO_CONNECTED,
                                 (Message)(&usb_audio->headset->audio_source))
    );
}

void UsbAudio_HandleAudioConfigChanged(usb_audio_info_t *usb_audio, usb_audio_status_message_t msg_id, bool reconnect_immediately)
{
    DEBUG_LOG_DEBUG("UsbAudio_HandleAudioConfigChanged");

    /* Always notify client regardless of chain being active or source is connected.
       Note that some host may disconnect the USB source momentarily to change sample size */

    if (UsbAudio_NotifyClient(USB_AUDIO_REGISTERED_CLIENT_CONFIG_CHANGE, msg_id,
                              (Message)(&usb_audio->headphone->audio_source)))
    {
        /* config change client is registered - nothing else to do */
    }
    else if (usb_audio->headphone->source_connected && usb_audio->headphone->chain_active)
    {
        /* No way to notify client, chain is active, so restart it. */
        UsbAudio_DisconnectAudioMsg(usb_audio);
        if (reconnect_immediately)
        {
            UsbAudio_ConnectAudioMsg(usb_audio);
        }
    }
}

void UsbAudio_HandleVoiceConfigChanged(usb_audio_info_t *usb_audio, usb_audio_status_message_t msg_id, bool reconnect_immediately)
{
    DEBUG_LOG_DEBUG("UsbAudio_HandleVoiceConfigChanged");

    /* Always notify client regardless of chain being active. */
    if (UsbAudio_NotifyClient(USB_AUDIO_REGISTERED_CLIENT_CONFIG_CHANGE,  msg_id,
                              (Message)(usb_audio->headset->audio_source)))
    {
        /* config change client is registered - nothing else to do */
    }
    else if (usb_audio->headset->source_connected && usb_audio->headset->chain_active)
    {
        /* No way to notify client, chain is active, so restart it.
         * Reconnection will be handled below*/
        UsbAudio_DisconnectVoiceMsg(usb_audio);
        if (reconnect_immediately)
        {
            UsbAudio_ConnectVoiceMsg(usb_audio);
        }
    }
}

Task UsbAudio_ClientRegister(Task client_task,
                             usb_audio_registered_client_t name)
{
    Task old_task = usb_audio_client_cb[name];

    usb_audio_client_cb[name]= client_task;

    return old_task;
}

void UsbAudio_ClientUnRegister(Task client_task,
                               usb_audio_registered_client_t name)
{
    if (usb_audio_client_cb[name] == client_task)
    {
        usb_audio_client_cb[name]= NULL;
    }
}
